fix(memory): stop rewriting the FTS5 trigger on every open (#366) - #979
Open
ziomik wants to merge 1 commit into
Open
fix(memory): stop rewriting the FTS5 trigger on every open (#366)#979ziomik wants to merge 1 commit into
ziomik wants to merge 1 commit into
Conversation
) _init_schema ran DROP TRIGGER + CREATE TRIGGER for reflections_au on every ReflectionStore open. Several components construct a store at daemon start, so two could overlap in that window: the loser hit "trigger reflections_au already exists", and the over-broad `except sqlite3.OperationalError` read that as "FTS5 is not compiled in" — leaving that process with keyword search silently degraded to LIKE for its whole lifetime. - Read sqlite_master first and skip the rewrite when the trigger is already current, which removes the window entirely for an up-to-date DB. A DB that genuinely needs migrating takes a BEGIN IMMEDIATE and re-checks under the lock, so racing openers serialize and exactly one performs the rewrite. (execute() statement by statement: executescript() would COMMIT implicitly.) - Narrow the except to "no such module" so only a missing FTS5 build degrades to LIKE; any other OperationalError now surfaces instead of being swallowed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #366.
Il problema
_init_schemaeseguivaDROP TRIGGER+CREATE TRIGGERsureflections_aua ogni apertura diReflectionStore. Diversi componenti ne costruiscono uno all'avvio del daemon, quindi due potevano sovrapporsi in quella finestra: il perdente ricevevatrigger reflections_au already existse l'except sqlite3.OperationalErrortroppo largo lo interpretava come "FTS5 non compilato in questa build".Risultato: quel processo restava con
_fts5_available = Falseper tutta la sua vita, conrecall()degradato aLIKE(niente ranking BM25) e nessun segnale oltre a una riga di warning che diceva la cosa sbagliata.Il fix
1. Non riscrivere il trigger quando e' gia' aggiornato (
_migrate_fts_update_trigger)Il trigger
reflections_aue' stato estratto da_FTS5_TRIGGERSin una costante propria: a differenza degli altri due non puo' usareCREATE ... IF NOT EXISTS, perche' i DB piu' vecchi hanno una versione che scattava a ogniUPDATE(l'access tracking faceva churn dell'indice FTS) e va davvero sostituita.Ora si legge prima
sqlite_master: se la definizione e' gia' quella corrente non si scrive nulla, quindi su un DB aggiornato la finestra di race sparisce del tutto. Un DB che ha davvero bisogno di migrare prende unBEGIN IMMEDIATEe ricontrolla sotto il lock, cosi' gli opener concorrenti si serializzano ed esattamente uno esegue la riscrittura.execute()statement per statement invece diexecutescript(), che farebbe un COMMIT implicito annullando la transazione.2. Restringere l'
exceptSolo
no such modulesignifica FTS5 assente: quel caso continua a degradare aLIKEcome da #295. Ogni altroOperationalError(errore di I/O, corruzione, timeout sul lock) ora propaga invece di essere scambiato per "FTS5 non disponibile".Test
Nuovo file
tests/test_memory_fts_init_race.py(5 test):disk I/O errornon viene scambiato per FTS5 mancanteIl test concorrente asserisce "esattamente una migrazione" tracciando l'SQL: e' deterministico proprio grazie al ricontrollo sotto lock, e senza quell'asserzione passerebbe anche con il codice pre-fix.
Verifica eseguita:
test_memory_store.py+test_memory_server.py-> 174 passed; le altre 8 suite che toccanopinky_memory(test_kg_*,test_memory_cross_agent,test_memory_heal_unembedded,test_shared_mcp) -> 290 passedruff checkpulito su entrambi i fileNote
Branch basato su
origin/main, non sulla linea di produzione (bloccata da GH013 per una chiave Supabase in history).Fuori scope, tracciato a parte in #368: anche
_migrate_backfill_review_scheduleesegue unaUPDATEincondizionata a ogni apertura dello store, non solo alla prima.🤖 Opened by Engineer